package com.moter.demo01.juc.jmm;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class SingleDemo {
    public static void main(String[] args) throws Exception {
        //反射破解单例模式
        Single1 single1 = Single1.getInstance();
        Constructor<Single1> declaredConstructor = Single1.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Field flag = Single1.class.getDeclaredField("flag");
        flag.setAccessible(true);
        flag.setBoolean(single1,Boolean.FALSE);
        Single1 single2 = declaredConstructor.newInstance(null);
        flag.setBoolean(single1,Boolean.FALSE);
        Single1 single3 = declaredConstructor.newInstance(null);
        System.out.println(single1);
        System.out.println(single2);
        System.out.println(single3);

        //验证new操作不是原子操作
//        for (int i = 0; i <100 ; i++) {
//            new Thread(()->{
//                try {
//                    TimeUnit.SECONDS.sleep(1);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                Single1.getInstance();
//            }).start();
//        }

        //枚举实现单例 无法被反射破坏
        Single2 instance = Single2.getInstance();
        Constructor<Single2> declaredConstructor1 = Single2.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor1.setAccessible(true);
        Single2 single21 = declaredConstructor1.newInstance("12",2);
        System.out.println(instance);
        System.out.println(single21);
    }
}

/**
 * 反射通过构造器可以破环单例模式
 */
class Single1{
    //如果不家volatile，会有问题，初始化对象分为三步：
    //分配内存空间，
    //初始化对象
    //对象指向该空间
    //如果发生指令重排，会出问题,(几率很小)

    //这样会消耗大量内存
//    private static Single1 single1 = new Single1();
    private static volatile Single1 single1;
    private static boolean flag=false;
    private Single1(){
        if(!flag){
            flag=true;
        }else{
            throw new RuntimeException("不要用反射来破坏我的单例模式");
        }
        System.out.println(Thread.currentThread().getName()+"\tOK");
    }
    public static Single1 getInstance(){
        if(single1==null){
            synchronized (Single1.class){
                if(single1==null){
                    single1=new Single1();
                }
            }
        }
        return single1;
    }
}
enum Single2{
    INSTANCE;
    private Single2(){}
    public static Single2 getInstance(){
        return INSTANCE;
    }
}